using System.Net.Http.Headers;
using System.Text;

namespace Ctyun.CF.SDK.Core;
public class CTApiClient
{
    public string Ak { get; private set; }
    public string Sk { get; private set; }
    public string Endpoint { get; private set; }

    public string? Region { get; private set; }

    private readonly HttpClient httpClient;

    private CTApiClient()
    {
        this.Ak = "";
        this.Sk = "";
        this.Endpoint = "";

        var handler = new HttpClientHandler();
        handler.ClientCertificateOptions = ClientCertificateOption.Manual;
        handler.ServerCertificateCustomValidationCallback =
            (httpRequestMessage, cert, cetChain, policyErrors) =>
        {
            return true;
        };

        this.httpClient = new HttpClient(handler);
    }

    public class Builder
    {
        private CTApiClient client = new CTApiClient();

        public Builder Ak(string ak)
        {
            client.Ak = ak;
            return this;
        }

        public Builder Sk(string sk)
        {
            client.Sk = sk;
            return this;
        }

        public Builder Endpoint(string endpoint)
        {
            client.Endpoint = endpoint;
            return this;
        }

        public Builder Region(string region)
        {
            client.Region = region;
            return this;
        }

        public CTApiClient Build()
        {
            return client;
        }
    }

    public string Request(string methodName, string path, HttpRequestHeaders headers, string body)
    {
        var method = HttpMethod.Parse(methodName);

        using (var request = new HttpRequestMessage(method, this.Endpoint + path))
        {
            var url = this.Endpoint + path;
            signHeader(request, headers, url, body);
            if (this.Region != "")
            {
                request.Headers.Add("regionId", this.Region);
            }
            if (body != null)
            {
                request.Content = new StringContent(body, Encoding.UTF8, "application/json");
            }
            var response = httpClient.Send(request);
            using var stream = response.Content.ReadAsStream();
            using var streamReader = new StreamReader(response.Content.ReadAsStream());
            string respRead = streamReader.ReadToEnd();
            if (!response.IsSuccessStatusCode)
            {
                throw new HttpRequestException($"Reuqest failed, status code: {(int)response.StatusCode}, message: {respRead}");
            }
            return respRead;
        }
    }

    private void signHeader(HttpRequestMessage request, HttpRequestHeaders headers, String url, String body)
    {
        var sortedQuery = getSortedQuery(url);
        var requestId = Guid.NewGuid().ToString();
        var eopDate = DateTime.Now.ToString("yyyyMMddTHHmmssZ");
        var eopAuthorization = CTSigner.GetEopAuthorization(eopDate, this.Ak, this.Sk, requestId, sortedQuery, body);

        request.Headers.Add("Eop-Date", eopDate);
        request.Headers.Add("Ctyun-Eop-Request-Id", requestId);
        request.Headers.Add("Eop-Authorization", eopAuthorization);
    }

    private string getSortedQuery(string url)
    {
        Uri uri = new Uri(url);
        string query = uri.Query;
        if (query.StartsWith("?"))
        {
            query = query.Substring(1);
        }

        Dictionary<string, List<string>> queryDict = new();
        foreach (string param in query.Split('&'))
        {
            string[] kv = param.Split('=');
            if (kv.Length == 2)
            {
                if (!queryDict.ContainsKey(kv[0]))
                {
                    queryDict[kv[0]] = new List<string>();
                }
                queryDict[kv[0]].Add(kv[1]);
            }
        }
        var sortedDict = queryDict.OrderBy(kv => kv.Key);
        string sortedQuery = "";
        foreach (var kv in sortedDict)
        {
            foreach (var value in kv.Value)
            {
                sortedQuery += $"{kv.Key}={value}&";
            }
        }
        if (sortedQuery.EndsWith("&"))
        {
            sortedQuery = sortedQuery.Substring(0, sortedQuery.Length - 1);
        }
        return sortedQuery;
    }
}